package com.md.util;

import com.google.common.collect.Lists;
import com.md.model.excel.MergeInfo;
import com.md.model.html.CustomLabel;
import com.md.model.html.LabelAttribute;
import com.md.model.html.LabelStyle;
import com.md.value.MergeType;
import com.md.value.ReadFileConstants;
import com.md.value.html.FontStyle;
import com.md.value.html.FontWeight;
import com.md.value.html.LabelAttributeType;
import com.md.value.html.LabelStyleType;
import com.md.value.html.TextAlign;
import com.md.value.html.TextDecoration;
import com.md.value.html.Unit;
import lombok.experimental.UtilityClass;
import org.apache.commons.compress.utils.Sets;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.FontUnderline;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.UnderlinePatterns;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBooleanProperty;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRElt;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.md.util.ColorUtil.convertToByteArray;
import static com.md.value.html.HtmlConstants.BASE_CODE;
import static com.md.value.html.HtmlConstants.CUSTOM_LABEL_END;

/**
 * @author 刘骄阳
 * 2024-05-15 23:56
 */
@UtilityClass
public class HtmlUtil {
  /**
   * 前端标题样式
   */
  public static final Map<String, LabelStyle> FONT_SIZE = new HashMap<>();

  /**
   * 需要往下递归的标签
   */
  public static final List<String> TABLE_LABEL = new ArrayList<>();
  /**
   * 字体大小分水岭; 小于400当细体, 大于当粗体, 等于就是正常
   */
  public static final int FONT_SIZE_SPLIT = 400;

  /**
   * H1标签字体大小(单位: px)
   */
  public static final double H1_FONT_SIZE = 32.0;

  /**
   * H2标签字体大小(单位: px)
   */
  public static final double H2_FONT_SIZE = 24.0;

  /**
   * H3标签字体大小(单位: px)
   */
  public static final double H3_FONT_SIZE = 18.72;

  /**
   * H4标签字体大小(单位: px)
   */
  public static final double H4_FONT_SIZE = 16.0;

  /**
   * H5标签字体大小(单位: px)
   */
  public static final double H5_FONT_SIZE = 13.28;

  /**
   * H6标签字体大小(单位: px)
   */
  public static final double H6_FONT_SIZE = 12.0;
  /**
   * 正则校验, 分离数值和单位
   */
  public static final Pattern PATTERN = Pattern.compile("([0-9]*[.]?\\d*)?(pt|px)?");

  static {
    FONT_SIZE.put("h1", LabelStyle.of(LabelStyleType.FONT_SIZE, H1_FONT_SIZE, Unit.PX));
    FONT_SIZE.put("h2", LabelStyle.of(LabelStyleType.FONT_SIZE, H2_FONT_SIZE, Unit.PX));
    FONT_SIZE.put("h3", LabelStyle.of(LabelStyleType.FONT_SIZE, H3_FONT_SIZE, Unit.PX));
    FONT_SIZE.put("h4", LabelStyle.of(LabelStyleType.FONT_SIZE, H4_FONT_SIZE, Unit.PX));
    FONT_SIZE.put("h5", LabelStyle.of(LabelStyleType.FONT_SIZE, H5_FONT_SIZE, Unit.PX));
    FONT_SIZE.put("h6", LabelStyle.of(LabelStyleType.FONT_SIZE, H6_FONT_SIZE, Unit.PX));

    TABLE_LABEL.add("table");
    TABLE_LABEL.add("figure");
    TABLE_LABEL.add("tbody");
  }

  /**
   * 获取属性样式
   *
   * @param attributes 属性
   * @return 属性样式code
   */
  public static String getAllAttributeCode(final List<LabelAttribute> attributes) {
    if (ObjectUtil.isEmpty(attributes)) {
      return "";
    }
    StringBuilder attributeCode = new StringBuilder();
    attributes.stream().filter(ObjectUtil::notEmpty).map(LabelAttribute::getAttributeCode)
      .forEach(attributeCode::append);
    return attributeCode.toString();
  }

  /**
   * 获取行内样式
   *
   * @param styles 行内样式
   * @return 行内样式code
   */
  public static String getAllStyleCode(final List<LabelStyle> styles) {
    if (ObjectUtil.isEmpty(styles)) {
      return "";
    }
    StringBuilder styleCode = new StringBuilder();
    styles.stream().filter(ObjectUtil::notEmpty).map(LabelStyle::getStyleCode).forEach(styleCode::append);
    return styleCode.toString();
  }

  /**
   * 生成html代码
   *
   * @param label      标签
   * @param styles     行内样式
   * @param attributes 属性
   * @return html代码
   */
  public static String generateHTMLCode(final String label,
                                        final List<LabelStyle> styles,
                                        final List<LabelAttribute> attributes,
                                        final String content) {
    return String.format(BASE_CODE, label, getAllAttributeCode(attributes), getAllStyleCode(styles)) +
      content +
      String.format(CUSTOM_LABEL_END, label);
  }


  /**
   * 构建自定义标签样式<优先使用标签>
   *
   * @param label      标签
   * @param content    内容
   * @param styles     样式列表
   * @param attributes 树形列表
   * @return 自定义标签
   */
  public static CustomLabel buildUseLabel(final String label, final String content, final List<LabelStyle> styles,
                                          final List<LabelAttribute> attributes) {
    CustomLabel customLabel = new CustomLabel(label);

    // 将样式转为map
    Map<LabelStyleType, LabelStyle> styleMap =
      CollectionUtil.toMap(styles, LabelStyle::getStyleType, Function.identity());

    // 获取可转标签的样式
    LabelStyle textDecoration = styleMap.remove(LabelStyleType.TEXT_DECORATION);

    // 判断是否加粗
    boolean isBlob = Optional.ofNullable(styleMap.get(LabelStyleType.FONT_WEIGHT)).map(LabelStyle::getValue)
      .map(Object::toString).map(blob -> blob.equals(FontWeight.BOLD.getValue())).orElse(false);
    if (isBlob) {
      styleMap.remove(LabelStyleType.FONT_WEIGHT);
    }

    // 是否倾斜
    boolean isItalic = Optional.ofNullable(styleMap.get(LabelStyleType.FONT_STYLE)).map(LabelStyle::getValue)
      .map(Object::toString).map(italic -> italic.equals(FontStyle.ITALIC.getValue())).orElse(false);
    if (isItalic) {
      styleMap.remove(LabelStyleType.FONT_STYLE);
    }
    // 设置本级内容
    Optional.ofNullable(attributes).orElse(Lists.newArrayList()).forEach(customLabel::addAttribute);
    styleMap.values().forEach(customLabel::addStyle);

    customLabel.setContent(content);
    if (ObjectUtil.notEmpty(textDecoration)) {
      String value = textDecoration.getValue().toString();
      if (value.contains(TextDecoration.UNDERLINE.getCode())) {
        customLabel = wrapLabel("u", customLabel);
      }
      if (value.contains(TextDecoration.LINE_THROUGH.getCode())) {
        customLabel = wrapLabel("s", customLabel);
      }
    }
    if (isBlob) {
      customLabel = wrapLabel("strong", customLabel);
    }
    if (isItalic) {
      customLabel = wrapLabel("i", customLabel);
    }
    return customLabel;
  }

  /**
   * 包裹标签
   *
   * @param label         标签
   * @param childrenLabel 子标签
   * @return 父级标签
   */
  public CustomLabel wrapLabel(final String label, final CustomLabel childrenLabel) {
    CustomLabel parent = new CustomLabel(label);
    parent.setChildrenList(Lists.newArrayList(childrenLabel));
    return parent;
  }

  public static void processFirstElement(final Element element, final XWPFDocument document) {
    XWPFParagraph pParagraph = document.createParagraph();

    if (element.tagName().equals("p")) {
      Elements firstLevelElements = element.children();

      if (firstLevelElements.isEmpty()) {
        XWPFRun pRun = pParagraph.createRun();
        pRun.setText(element.text());
      } else {
        for (Element child : firstLevelElements) {
          XWPFRun pRun = pParagraph.createRun();
          switch (child.tagName()) {
            case "span":
              analysisSpan(firstLevelElements, pParagraph);
              break;
            case "s":
              pRun.setStrikeThrough(true);
              pRun.setText(child.text());
              break;
            case "u":
              pRun.setUnderline(UnderlinePatterns.SINGLE);
              pRun.setText(child.text());
              break;
            case "strong":
              pRun.setBold(true);
              pRun.setText(child.text());
              break;
            default:
              pRun.setText(child.text());
              break;

          }
        }
      }

//      XWPFRun pRun = pParagraph.createRun();
//      Elements s = element.getElementsByTag("s");
//      // 获取u标签
//      Elements u = element.getElementsByTag("u");
//      Elements strong = element.getElementsByTag("strong");
//      // 获取span标签中的样式信息并应用到文本运行中
//      Elements spans = element.getElementsByTag("span");
//      if (!spans.isEmpty()) {
//        analysisSpan(spans, pParagraph);
//      }
//      if (!s.isEmpty()){
//        XWPFRun sRun = pParagraph.createRun();
//        sRun.setStrikeThrough(true);
//        sRun.setText(s.text());
//      }
//      if (!u.isEmpty()){
//        XWPFRun uRun = pParagraph.createRun();
//        uRun.setStrikeThrough(true);
//        uRun.setText(u.text());
//      }
//      if (!strong.isEmpty()){
//        XWPFRun strongRun = pParagraph.createRun();
//        strongRun.setBold(true);
//        strongRun.setText(strong.text());
//      }
//      if (s.isEmpty() && u.isEmpty() && strong.isEmpty() && spans.isEmpty()){
//        pRun.setText(element.text());
//      }
    } else if (element.tagName().startsWith("h")) {
      Elements spans = element.getElementsByTag("span");
      if (!spans.isEmpty()) {
        analysisSpan(spans, pParagraph);
      } else {
        XWPFRun hrRun = pParagraph.createRun();
        hrRun.setText(element.text());
      }
    } else {
      XWPFRun hrRun = pParagraph.createRun();
      hrRun.setText(element.text());
    }
  }

  private static void analysisSpan(Elements spans, XWPFParagraph pParagraph) {
    for (Element spanElement : spans) {
      XWPFRun spanRun = pParagraph.createRun();
      spanRun.setText(spanElement.text());
      if (spanElement.hasAttr("styles")) {
        String style = spanElement.attr("styles");
        String color = extractStyleAttribute(style, "color");
        String fontSize = extractStyleAttribute(style, "font-size");
        String fontFamily = extractStyleAttribute(style, "font-family");
        if (fontSize != null && !fontSize.isEmpty()) {
          spanRun.setFontSize(convertFontSizeToHalfPoints(fontSize));
        }
        if (fontFamily != null && !fontFamily.isEmpty()) {
          spanRun.setFontFamily(fontFamily);
        }
        if (color != null && !color.isEmpty()) {
          int r = Integer.parseInt(color.split(",")[0].substring(color.indexOf("(") + 1));
          int g = Integer.parseInt(color.split(",")[1].trim());
          int b = Integer.parseInt(color.split(",")[2].trim()
            .substring(0, color.split(",")[2].length() - 2));
          String hexValue = String.format("%02X%02X%02X", r, g, b);
          spanRun.setColor(hexValue);
        }
        // 可以根据需要处理更多样式属性
      }
      Elements s = spanElement.getElementsByTag("s");
      if (!s.isEmpty()) {
        spanRun.setStrikeThrough(true);
      }
      // 获取u标签
      Elements u = spanElement.getElementsByTag("u");
      if (!u.isEmpty()) {
        spanRun.setUnderline(UnderlinePatterns.SINGLE);
      }
      Elements strong = spanElement.getElementsByTag("strong");
      if (!strong.isEmpty()) {
        spanRun.setBold(true);
      }
    }
  }

  private String extractStyleAttribute(String style, String attribute) {
    String[] styles = style.split(";");
    for (String s : styles) {
      String[] keyValue = s.split(":");
      if (keyValue.length == 2 && keyValue[0].trim().equals(attribute)) {
        return keyValue[1].trim();
      }
    }
    return null;
  }

  private int convertFontSizeToHalfPoints(String fontSize) {
    // 去掉末尾的px
    if (fontSize.endsWith("px")) {
      fontSize = fontSize.replace("px", "").trim();
    }
    // 将font-size转换为半点磅值
    try {
      int sizeInPixels = Integer.parseInt(fontSize);
      return sizeInPixels * 2; // 假设1px = 0.5pt
    } catch (NumberFormatException e) {
      // 处理转换错误，返回默认值
      return 22; // 11pt * 2 = 22 half-points
    }
  }

  public static void convertEntityToExcel(final List<CustomLabel> labels) throws IOException {

    try (XSSFWorkbook workbook = new XSSFWorkbook()) {
      // 创建工作簿
      XSSFSheet sheet = workbook.createSheet();
      convertEntityToExcel(labels, sheet, workbook);
      List<CellRangeAddress> merge = new ArrayList<>();

      // 行的个数
      List<CustomLabel> trLabels = new ArrayList<>();
      getTableRow(labels, trLabels);
      createMerge(trLabels, 0, merge);
      supplementTable(trLabels, merge);

      // 3. 将实体转为excel
      convertEntityToExcel(trLabels, sheet, workbook);
      createMerge(labels, null, merge);
      merge.forEach(sheet::addMergedRegion);
      try (FileOutputStream outputStream = new FileOutputStream("D://代码导出.xlsx")) {
        workbook.write(outputStream);
        // 刷新和关闭输出流
        outputStream.flush();
      }
    }
  }

  private static void getTableRow(List<CustomLabel> labels, List<CustomLabel> results) {
    labels.forEach(baseLabel -> {
      if (TABLE_LABEL.contains(baseLabel.getLabel())) {
        getTableRow(baseLabel.getChildrenList(), results);
      } else {
        results.add(baseLabel);
      }
    });
  }

  /**
   * 补充td
   *
   * @param customLabels 标签
   * @param merges       合并的数据
   */
  public static void supplementTable(final List<CustomLabel> customLabels, final List<CellRangeAddress> merges) {
    List<MergeInfo> mergeInfoList = MergeInfo.readMergeInfo(merges);
    mergeInfoList.stream().collect(Collectors.groupingBy(MergeInfo::getRowIndex)).forEach((rowIndex, mergeInfos) -> {
      mergeInfos.stream().sorted(Comparator.comparing(MergeInfo::getCellIndex)).forEach(mergeInfo -> {
        if (mergeInfo.getType() == MergeType.CONTINUE) {
          // 如果是跳过,说明该位置被跳过了. 那么就在该td中新增一个对应位置的数据, 因为excel和html展示不太一样
          CustomLabel customLabel = customLabels.get(rowIndex);
          List<CustomLabel> childrenList = customLabel.getChildrenList();
          if (childrenList.size() < mergeInfo.getCellIndex()) {
            childrenList.add(new CustomLabel(""));
          } else {
            childrenList.add(mergeInfo.getCellIndex(), new CustomLabel(""));

          }
        }
      });
    });
  }

  private static void createMerge(final List<CustomLabel> labels,
                                  final Integer rowIndex,
                                  final List<CellRangeAddress> mergeList) {
    int maxRow = labels.size();
    // 创建工作簿
    for (int i = 0; i < maxRow; i++) {
      // 每一个 0 都是根级都算作一行
      CustomLabel customLabel = labels.get(i);
      String label = customLabel.getLabel();
      if ("tr".equals(label)) {
        // 如果遇到tr, 往下递归, 并将当前行数赋予
        createMerge(customLabel.getChildrenList(), i, mergeList);
      } else if ("td".equals(label)) {
        Map<LabelAttributeType, LabelAttribute> attributeMap = Optional.ofNullable(customLabel.getAttributes())
          .map(labelAttributes -> labelAttributes.stream().collect(Collectors.toMap(LabelAttribute::getAttribute,
            Function.identity()))).orElse(new HashMap<>());

        // 判断当前是第几行第几列
        int endRow = Optional.ofNullable(attributeMap.get(LabelAttributeType.ROWSPAN)).map(labelAttribute ->
          labelAttribute.getValue().toString()).map(Integer::parseInt).map(row -> row - 1).orElse(0);
        int endCell = Optional.ofNullable(attributeMap.get(LabelAttributeType.COLSPAN)).map(labelAttribute ->
          labelAttribute.getValue().toString()).map(Integer::parseInt).map(cell -> cell - 1).orElse(0);
        if (endRow != 0 || endCell != 0) {
          mergeList.add(new CellRangeAddress(rowIndex, rowIndex + endRow, i, i + endCell));
        }
      } else {
        // 如果是其他类型的标签, 无需赋予行数
        createMerge(customLabel.getChildrenList(), rowIndex, mergeList);
      }
    }
  }

  public static void convertEntityToExcel(final List<CustomLabel> labels, final XSSFSheet sheet,
                                          final XSSFWorkbook workbook) {
    int maxRow = labels.size();
    // 创建工作簿
    for (int i = 0; i < maxRow; i++) {
      // 每一个 0 都是根级都算作一行
      CustomLabel customLabel = labels.get(i);
      String label = customLabel.getLabel();
      // 判断内部是否有tr/td, 如果有, 就说明有很多行很多列, 如果没有那就说明都扔到同一个单元格内.
      // 如果有tr/td 那就, 单独新增一个行
      if ("table".equals(label) || "figure".equals(label) || "tbody".equals(label)) {
        convertEntityToExcel(customLabel.getChildrenList(), sheet, workbook);
      } else if ("tr".equals(label)) {
        // 如果遇到tr, 往下递归
        sheet.createRow(sheet.getLastRowNum() + 1);
        convertEntityToExcel(customLabel.getChildrenList(), sheet, workbook);
      } else {
        XSSFRow row;
        XSSFCell cell;
        if ("td".equals(label)) {
          // 说明要跳列, 最后一行的最后一列
          row = sheet.getRow(sheet.getLastRowNum());
          int cellIndex = row.getLastCellNum();
          if (cellIndex == -1) {
            cellIndex++;
          }
          cell = row.createCell(cellIndex, CellType.STRING);
        } else {
          // 新建行,展示
          row = sheet.createRow(sheet.getLastRowNum() + 1);
          cell = row.createCell(0, CellType.STRING);
        }
        cell.setCellStyle(setCellStyle(workbook, customLabel.getAllStyle()));
        if (customLabel.getChildrenList().isEmpty()) {
          cell.setCellValue(customLabel.getContent());
        } else {
          cell.setCellValue(buildRichTextString(customLabel, workbook));
        }
      }
    }
  }

  public static XSSFRichTextString buildRichTextString(final CustomLabel customLabel, final XSSFWorkbook workbook) {
    XSSFRichTextString richTextString = new XSSFRichTextString(customLabel.getContent());
    recursionSetFontToRichTextString(customLabel.getChildrenList(), richTextString, workbook);
    richTextString.getCTRst().getRList().forEach(ctrElt ->
      Optional.ofNullable(ctrElt).map(CTRElt::getRPr).ifPresent(ctrPrElt -> {
        // 加粗
        ctrPrElt.getBList().forEach(CTBooleanProperty::unsetVal);
        // 倾斜
        ctrPrElt.getIList().forEach(CTBooleanProperty::unsetVal);
        // 删除线
        ctrPrElt.getStrikeList().forEach(CTBooleanProperty::unsetVal);
      }));
    return richTextString;
  }


  public static XSSFCellStyle setCellStyle(final XSSFWorkbook workbook, final List<LabelStyle> styles) {
    XSSFCellStyle cellStyle = workbook.createCellStyle();
    if (styles != null && !styles.isEmpty()) {
      styles.forEach(labelStyle -> {
        String value = labelStyle.getValue().toString();
        switch (labelStyle.getStyleType()) {
          case TEXT_ALIGN:
            cellStyle.setAlignment(TextAlign.of(labelStyle.getValue().toString()).convertToExcel());
            break;
          case BACKGROUND_COLOR:
            cellStyle.setFillForegroundColor(new XSSFColor(convertToByteArray(value)));
            break;
          default:
            break;
        }
      });
    }
    cellStyle.setFont(setFont(workbook, styles));
    return cellStyle;
  }

  public static XSSFFont setFont(final XSSFWorkbook workbook, final List<LabelStyle> styles) {
    XSSFFont font = workbook.createFont();
    if (styles != null && !styles.isEmpty()) {
      styles.forEach(labelStyle -> {
        String value = labelStyle.getValue().toString();
        switch (labelStyle.getStyleType()) {
          case COLOR:
            font.setColor(new XSSFColor(convertToByteArray(value)));
            break;
          case FONT_SIZE:
            font.setFontHeightInPoints(labelStyle.getUnit().convert(Unit.PT, Double.parseDouble(value)).
              shortValueExact());
            break;
          case FONT_STYLE:
            boolean isSetItalic = FontStyle.ITALIC.getValue().equals(value);
            font.setItalic(isSetItalic);
//            if (isSetItalic) {
//              font.getCTFont().getIList().forEach(CTBooleanProperty::unsetVal);
//            }
            break;
          case FONT_FAMILY:
            font.setFontName(value);
            break;
          case TEXT_DECORATION:
            List<String> textDecorationArray = Arrays.asList(value.split(" "));
            if (!textDecorationArray.contains(TextDecoration.NONE.getCode())) {
              for (final String textDecoration : textDecorationArray) {
                if (TextDecoration.UNDERLINE.getCode().equals(textDecoration)) {
                  font.setUnderline(FontUnderline.SINGLE);
                }
                if (TextDecoration.LINE_THROUGH.getCode().equals(textDecoration)) {
                  font.setStrikeout(true);
                }
              }
            }
            break;
          case FONT_WEIGHT:
            boolean isSetBlob = FontWeight.BOLD.getValue().equals(value);
            font.setBold(isSetBlob);
//            if (isSetBlob) {
//              font.getCTFont().getBList().forEach(CTBooleanProperty::unsetVal);
//            }
            break;
          default:
            break;
        }
      });
    }
    return font;
  }

  public static void recursionSetFontToRichTextString(final List<CustomLabel> childrensList,
                                                      final XSSFRichTextString richTextString,
                                                      final XSSFWorkbook workbook) {
    childrensList.forEach(baseLabel -> {
      if (baseLabel.getContent() != null) {
        richTextString.append(baseLabel.getContent(), setFont(workbook, baseLabel.getAllStyle()));
      }
      if (!baseLabel.getChildrenList().isEmpty()) {
        recursionSetFontToRichTextString(baseLabel.getChildrenList(), richTextString, workbook);
      }
    });
  }

  public static void analysisHtmlCode(final String htmlCode) throws IOException {
    Document document = Jsoup.parse(htmlCode);
    // 获取body中的document
    Elements children = document.body().children();
    List<CustomLabel> customLabels = analysisHtmlToEntity(children, 0, new HashMap<>());
    convertEntityToExcel(customLabels);
    System.out.println();
  }

  public static List<CustomLabel> analysisHtmlToEntity(final Elements elements, final Integer level,
                                                       final Map<String, LabelStyle> parentStyle) {
    List<CustomLabel> list = new ArrayList<>();
    if (elements == null) {
      return list;
    }
    for (final Element element : elements) {
      // 文本内容
      String content = Optional.ofNullable(element.textNodes()).orElse(new ArrayList<>()).stream().
        map(TextNode::text).collect(Collectors.joining());
      // 标签
      String label = element.tagName();
      Attributes attributes = element.attributes();

      CustomLabel customLabel = new CustomLabel(label);
      customLabel.setContent(content);
      // 样式
      setLabelStyle(attributes.get("styles"), label, parentStyle).forEach(customLabel::addStyle);
      // 属性
      attributes.asList().forEach(attribute -> {
        LabelAttributeType labelAttributeType = LabelAttributeType.of(attribute.getKey());
        if (labelAttributeType == null) {
          return;
        }
        customLabel.addAttribute(LabelAttribute.of(labelAttributeType, attribute.getValue()));
      });

      customLabel.setLevel(level);

      Map<String, LabelStyle> currentParentStyle = new HashMap<>(parentStyle);
      // 覆盖父级的样式, todo 有问题, 下划线和删除线
      currentParentStyle.putAll(CollectionUtil.toMap(customLabel.getStyles(),
        LabelStyle::getStyleName, Function.identity()));

      customLabel.setChildrenList(analysisHtmlToEntity(element.children(), level + 1, currentParentStyle));
      list.add(customLabel);
    }
    return list;
  }

  public static boolean isNumeric(final String value) {
    return value.matches("\\d+");
  }

  public static List<LabelStyle> setLabelStyle(final String styleHtml,
                                               final String label,
                                               final Map<String, LabelStyle> parentStyle) {
    List<LabelStyle> labelStyleList = new ArrayList<>();

    List<LabelStyleType> filter = new ArrayList<>();
    if (styleHtml != null && !styleHtml.isEmpty()) {
      String[] styleArray = styleHtml.split(";");
      for (final String styleMap : styleArray) {
        String[] style = styleMap.split(":");
        // 未处理单位
        Optional.ofNullable(LabelStyleType.of(style[0])).ifPresent(labelStyleType -> {
          filter.add(labelStyleType);
          String value = style[1];
          switch (labelStyleType) {
            case FONT_SIZE:
              // 使用正则表达式分割字符串
              labelStyleList.add(buildFileSize(value));
              break;
            case FONT_WEIGHT:
              if (isNumeric(value)) {
                int fontWeightValue = Integer.parseInt(value);
                if (fontWeightValue < 400) {
                  labelStyleList.add(LabelStyle.of(labelStyleType, FontWeight.LIGHTER.getValue()));
                } else if (fontWeightValue > 400) {
                  labelStyleList.add(LabelStyle.of(labelStyleType, FontWeight.BOLD.getValue()));
                } else {
                  labelStyleList.add(LabelStyle.of(labelStyleType, FontWeight.NORMAL.getValue()));
                }
              } else {
                labelStyleList.add(LabelStyle.of(labelStyleType, value));
              }
              break;
            default:
              labelStyleList.add(LabelStyle.of(labelStyleType, value));
              break;
          }
        });
      }
    }
    Optional.ofNullable(isItalic(label)).filter(labelStyle -> !filter.contains(labelStyle.getStyleType()))
      .ifPresent(labelStyleList::add);
    Optional.ofNullable(isBlob(label)).filter(labelStyle -> !filter.contains(labelStyle.getStyleType()))
      .ifPresent(labelStyleList::add);
    isSetFontSize(label).stream().filter(labelStyle -> !filter.contains(labelStyle.getStyleType()))
      .forEach(labelStyleList::add);
    // 下划线和删除线要同时判断, 因为 样式是一样的,但是标签不一样.
    isTextDecoration(label, parentStyle.get(LabelStyleType.TEXT_DECORATION.getStyleName())).ifPresent(labelStyleList::add);
    return labelStyleList;
  }

  /**
   * 生成文字大小
   *
   * @param value 读取出来的文字
   * @return style实体
   */
  public static LabelStyle buildFileSize(final String value) {
    // 使用正则表达式分割字符串
    Pattern pattern = Pattern.compile("([0-9]*[.]?\\d*)?(pt|px)?");
    Matcher matcher = pattern.matcher(value);
    if (matcher.matches()) {
      return LabelStyle.of(LabelStyleType.FONT_SIZE, matcher.group(1), Unit.of(matcher.group(2)));
    }
    return null;
  }


  public static LabelStyle isItalic(final String label) {
    if ("i".equals(label) || "em".equals(label)) {
      return LabelStyle.of(LabelStyleType.FONT_STYLE, FontStyle.ITALIC.getValue());
    }
    return null;
  }

  public static LabelStyle isBlob(final String label) {
    if ("b".equals(label) || "strong".equals(label)) {
      return LabelStyle.of(LabelStyleType.FONT_WEIGHT, FontWeight.BOLD.getValue());
    }
    return null;
  }

  public static List<LabelStyle> isSetFontSize(final String label) {
    Map<String, LabelStyle> fontSize = new HashMap<>();
    fontSize.put("h1", LabelStyle.of(LabelStyleType.FONT_SIZE, 32, Unit.PX));
    fontSize.put("h2", LabelStyle.of(LabelStyleType.FONT_SIZE, 24, Unit.PX));
    fontSize.put("h3", LabelStyle.of(LabelStyleType.FONT_SIZE, 18.72, Unit.PX));
    fontSize.put("h4", LabelStyle.of(LabelStyleType.FONT_SIZE, 16, Unit.PX));
    fontSize.put("h5", LabelStyle.of(LabelStyleType.FONT_SIZE, 13.28, Unit.PX));
    fontSize.put("h6", LabelStyle.of(LabelStyleType.FONT_SIZE, 12, Unit.PX));
    LabelStyle labelStyle = fontSize.get(label);
    if (labelStyle != null) {
      List<LabelStyle> arrayList = new ArrayList<>();
      arrayList.add(labelStyle);
      arrayList.add(LabelStyle.of(LabelStyleType.FONT_WEIGHT, FontWeight.BOLD.getValue()));
      return arrayList;
    }
    return new ArrayList<>();

  }

  /**
   * 获取下划线,删除线样式
   *
   * @param label      标签
   * @param labelStyle 父级样式
   * @return 当前样式
   */
  public static Optional<LabelStyle> isTextDecoration(final String label, final LabelStyle labelStyle) {
    // 父级的样式
    String parentTextDecoration =
      Optional.ofNullable(labelStyle).map(LabelStyle::getValue).map(Object::toString).orElse("");
    // 拼接子集的样式
    StringBuilder textDecoration = new StringBuilder(parentTextDecoration);
    if ("u".equals(label) || "ins".equals(label)) {
      textDecoration.append(TextDecoration.UNDERLINE.getCode()).append(" ");
    }
    if ("s".equals(label) || "del".equals(label)) {
      textDecoration.append(TextDecoration.LINE_THROUGH.getCode());
    }
    if (textDecoration.length() == 0) {
      return Optional.empty();
    }
    String[] textDecorationArray = textDecoration.toString().split(" ");
    return Optional.of(LabelStyle.of(LabelStyleType.TEXT_DECORATION,
      String.join(" ", Sets.newHashSet(textDecorationArray))));
  }

  public static CustomLabel createTabLabel(final String label) {
    CustomLabel tab = new CustomLabel(label);
    tab.setContent(ReadFileConstants.TAB);
    return tab;
  }


  public static void main(final String[] args) throws Exception {
    String filePath = "D://htmlCode.txt";
    StringBuilder htmlCode = new StringBuilder();
    try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
      String line;
      while ((line = reader.readLine()) != null) {
        htmlCode.append(line);
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
    analysisHtmlCode(htmlCode.toString());
  }
}
